/*
 *  This file contains all assembly code for the
 *  LM32 implementation of RTEMS.
 *
 *  Derived from no_cpu/cpu_asm.S, copyright (c) 1989-1999,
 *    On-Line Applications Research Corporation (OAR).
 *
 *  The license and distribution terms for this file may be
 *  found in the file LICENSE in this distribution or at
 *  http://www.rtems.org/license/LICENSE.
 *
 *  Jukka Pietarinen <jukka.pietarinen@mrf.fi>, 2008,
 *  Micro-Research Finland Oy
 *
 *  Michael Walle <michael@walle.cc>, 2009
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <rtems/asm.h>
#include <rtems/score/cpu_asm.h>

/*  void _CPU_Context_switch(run_context, heir_context)
 *
 *  This routine performs a normal non-FP context switch.
 *
 *  LM32 Specific Information:
 *
 *  Saves/restores all callee-saved general purpose registers as well as
 *  the stack pointer, return address and interrupt enable status register
 *  to/from the context.
 *
 */
    .globl _CPU_Context_switch
_CPU_Context_switch:
    sw      (r1+0), r11   /* r1 is the first argument */
    sw      (r1+4), r12
    sw      (r1+8), r13
    sw      (r1+12), r14
    sw      (r1+16), r15
    sw      (r1+20), r16
    sw      (r1+24), r17
    sw      (r1+28), r18
    sw      (r1+32), r19
    sw      (r1+36), r20
    sw      (r1+40), r21
    sw      (r1+44), r22
    sw      (r1+48), r23
    sw      (r1+52), r24
    sw      (r1+56), r25
    sw      (r1+60), gp
    sw      (r1+64), fp
    sw      (r1+68), sp
    sw      (r1+72), ra
    rcsr    r3, IE
    sw      (r1+76), r3
    .extern _exception_stack_frame
    mvhi    r3, hi(_exception_stack_frame)
    ori     r3, r3, lo(_exception_stack_frame)
    lw      r4, (r3+0)
    be      r4, r0, 2f
1:
    lw      r5, (r4+44)
    sw      (r3+0), r0
    bi      3f
2:
    mvhi    r5, hi(_Thread_Dispatch)
    ori     r5, r5, lo(_Thread_Dispatch)
3:
    sw      (r1+80), r5

_CPU_Context_switch_restore:
    lw      r11, (r2+0)   /* r2 is the second argument */
    lw      r12, (r2+4)
    lw      r13, (r2+8)
    lw      r14, (r2+12)
    lw      r15, (r2+16)
    lw      r16, (r2+20)
    lw      r17, (r2+24)
    lw      r18, (r2+28)
    lw      r19, (r2+32)
    lw      r20, (r2+36)
    lw      r21, (r2+40)
    lw      r22, (r2+44)
    lw      r23, (r2+48)
    lw      r24, (r2+52)
    lw      r25, (r2+56)
    lw      gp, (r2+60)
    lw      fp, (r2+64)
    lw      sp, (r2+68)
    lw      ra, (r2+72)
    lw      r3, (r2+76)
    wcsr    IE, r3
    ret

/*
 *  _CPU_Context_restore
 *
 *  This routine is generally used only to restart self in an
 *  efficient manner.  It may simply be a label in _CPU_Context_switch.
 *
 *  LM32 Specific Information:
 *
 *  Moves argument #1 to #2 and branches to the restore part of the
 *  context switch code above.
 */
    .globl _CPU_Context_restore
_CPU_Context_restore:
    mv      r2, r1
    bi      _CPU_Context_switch_restore

/*  void _ISR_Handler()
 *
 *  This routine provides the RTEMS interrupt management.
 *
 *  LM32 Specific Information:
 *
 *  Saves all the caller-saved general purpose registers as well as the
 *  return address, exception return address and breakpoint return address
 *  (the latter may be unnecessary) onto the stack, which is either the task
 *  stack (in case of a interrupted task) or the interrupt stack (if an
 *  interrupt was interrupted).
 *  After that, it figures out the pending interrupt with the highest
 *  priority and calls the main ISR handler written in C, which in turn
 *  handles interrupt nesting, software interrupt stack setup etc and
 *  finally calls the user ISR.
 *  At the end the saved registers are restored.
 *
 */

    .globl  _ISR_Handler
_ISR_Handler:
    xor      r0, r0, r0
    addi     sp, sp, -52
    sw       (sp+4), r1
    sw       (sp+8), r2
    sw       (sp+12), r3
    sw       (sp+16), r4
    sw       (sp+20), r5
    sw       (sp+24), r6
    sw       (sp+28), r7
    sw       (sp+32), r8
    sw       (sp+36), r9
    sw       (sp+40), r10
    sw       (sp+44), ra
    sw       (sp+48), ea
    sw       (sp+52), ba

    /*
     * Scan through IP & IM bits starting from LSB until irq vector is
     * found. The vector is stored in r1, which is the first argument for
     * __ISR_Handler.
     */
    rcsr    r2, IP
    rcsr    r3, IM
    mv      r1, r0        /* r1: counter for the vector number */
    and     r2, r2, r3    /* r2: pending irqs, which are enabled */
    mvi     r3, 1         /* r3: register for the walking 1 */
    /*
     * If r2 is zero, there was no interrupt.
     * This should never happen!
     */
    be      r2, r0, exit_isr
find_irq:
    and     r4, r2, r3
    bne     r4, r0, found_irq
    sli     r3, r3, 1
    addi    r1, r1, 1
    bi      find_irq

found_irq:
    /*
     * Call __ISR_Handler for further processing.
     * r1 is the vector number, calculated above
     * r2 is the pointer to the CPU_Interrupt_frame
     */
    addi    r2, sp, 4

    .extern __ISR_Handler
    mvhi    r3, hi(__ISR_Handler)
    ori     r3, r3, lo(__ISR_Handler)
    call    r3

exit_isr:
    /* Restore the saved registers */
    lw      r1, (sp+4)
    lw      r2, (sp+8)
    lw      r3, (sp+12)
    lw      r4, (sp+16)
    lw      r5, (sp+20)
    lw      r6, (sp+24)
    lw      r7, (sp+28)
    lw      r8, (sp+32)
    lw      r9, (sp+36)
    lw      r10, (sp+40)
    lw      ra, (sp+44)
    lw      ea, (sp+48)
    lw      ba, (sp+52)
    addi    sp, sp, 52
    eret

